﻿using System;
using System.Threading;

namespace XchangeStreamer.Core
{
    [Serializable]
    public static class Synchronizer
    {
        internal static void EnterReadOnlyMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            bool lockAcquired = false;
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                while (!lockAcquired)
                {
                    lockAcquired = locker.TryEnterReadLock(1000);
                }
            }
            else
            {
                while (!lockAcquired)
                {
                    lockAcquired = Monitor.TryEnter(locker, 1000);
                }
            }
        }

        internal static void EnterUpgradeableMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            bool lockAcquired = false;
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                while (!lockAcquired)
                {
                    lockAcquired = locker.TryEnterUpgradeableReadLock(1000);
                }
            }
            else
            {
                while (!lockAcquired)
                {
                    lockAcquired = Monitor.TryEnter(locker, 1000);
                }
            }
        }

        internal static void EnterWriteMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            bool lockAcquired = false;
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                while (!lockAcquired)
                {
                    lockAcquired = locker.TryEnterWriteLock(1000);
                }
            }
            else
            {
                while (!lockAcquired)
                {
                    lockAcquired = Monitor.TryEnter(locker, 1000);
                }
            }
        }

        internal static void ExitReadOnlyMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                if (locker.IsReadLockHeld)
                {
                    locker.ExitReadLock();
                }
            }
            else
            {
                Monitor.Exit(locker);
            }
        }

        internal static void ExitUpgradeableMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                if (locker.IsUpgradeableReadLockHeld)
                {
                    locker.ExitUpgradeableReadLock();
                }
            }
            else
            {
                Monitor.Exit(locker);
            }
        }

        internal static void ExitWriteMode(ReaderWriterLockSlim locker, LockType lockType)
        {
            if (lockType == LockType.ReaderWriterLockSlim)
            {
                if (locker.IsWriteLockHeld)
                {
                    locker.ExitWriteLock();
                }
            }
            else
            {
                Monitor.Exit(locker);
            }
        }

        internal static void Exit(ReaderWriterLockSlim locker, LockType lockType)
        {
            ExitWriteMode(locker, lockType);
            ExitUpgradeableMode(locker, lockType);
            ExitReadOnlyMode(locker, lockType);
        }

        public static ReaderWriterLockSlim GetSynchronizerInstance(LockRecursionPolicy recursionPolicy)
        {
            return new ReaderWriterLockSlim(recursionPolicy);
        }
    }

    public abstract class BaseSynchronizer : IDisposable
    {
        protected ReaderWriterLockSlim locker;
        protected LockType lockType;
        public bool HasLock { get; protected set; }

        public BaseSynchronizer(ReaderWriterLockSlim locker, LockType lockType)
        {
            this.locker = locker;
            this.lockType = lockType;
            this.HasLock = false;
        }

        public abstract void Dispose();
    }
    
    public class UpgradeableSynchronizer : BaseSynchronizer
    {
        public UpgradeableSynchronizer(ReaderWriterLockSlim locker, LockType lockType)
            : base(locker, lockType)
        {
            Synchronizer.EnterUpgradeableMode(this.locker, this.lockType);
            this.HasLock = true;
        }
        
        public override void Dispose()
        {
            Synchronizer.ExitUpgradeableMode(this.locker, this.lockType);
            this.HasLock = false;
        }
    }


    public class ReadOnlySynchronizer : BaseSynchronizer
    {
        public ReadOnlySynchronizer(ReaderWriterLockSlim locker, LockType lockType)
            : base(locker, lockType)
        {
            Synchronizer.EnterReadOnlyMode(this.locker, this.lockType);
            this.HasLock = true;
        }
        
        public override void Dispose()
        {
            Synchronizer.ExitReadOnlyMode(this.locker, this.lockType);
            this.HasLock = false;
        }
    }


    public class WritableSynchronizer : BaseSynchronizer
    {
        public WritableSynchronizer(ReaderWriterLockSlim locker, LockType lockType)
            : base(locker, lockType)
        {
            Synchronizer.EnterWriteMode(this.locker, this.lockType);
            this.HasLock = true;
        }
        
        public override void Dispose()
        {
            Synchronizer.ExitWriteMode(this.locker, this.lockType);
            this.HasLock = false;
        }
    }
}